package com.domcer.ultra.menu.application.loader;

import acl.ILoader;
import com.domcer.ultra.menu.BootStrap;
import com.domcer.ultra.menu.application.cache.MenuCache;
import com.domcer.ultra.menu.engine.MenuManager;
import com.domcer.ultra.menu.domain.MenuDomain;
import com.domcer.ultra.menu.domain.MenuSettingsDomain;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;

/**
 * @Author hang.wang
 * @Date 2025/1/31 00:19
 */
public class MenuCommandLoader implements ILoader {

    /**
     * this method executor based on {@link MenuFileLoader} <br>
     * because of {@link MenuCache} datasource from it
     */
    @Override
    public void execute0() {
        List<MenuDomain> menuDomainList = MenuCache.getMenuDomainList();
        menuDomainList.stream().map(MenuDomain::getMenuSettings)
                .map(MenuSettingsDomain::getCommands)
                .flatMap(List::stream)
                .forEach(this::registerCommand);
    }

    /**
     * all menu command executor is the same implement <br>
     * - 1. instanceof {@link Player} <br>
     * - 2. get {@link MenuDomain} from {@link MenuCache} <br>
     * - 3. open it
     * @return {@link CommandExecutor}
     */
    private static CommandExecutor getCommandExecutor() {
        return (commandSender, command, s, strings) -> {
            if (commandSender instanceof Player) {
                Player player = (Player) commandSender;
                MenuDomain menuDomain = MenuCache.getCommandMenuDomain(s);
                MenuManager.open(player, menuDomain);
            }
            return true;
        };
    }

    /**
     * through java reflect functional to register bukkit command <br>
     * not find the better method in temporary <br>
     * is not an elegant way to get declared field <br>
     * if not register custom menu command, in the high version bukkit server <br>
     * command suggest holder will prompt for red information
     * @param name command
     */
    private void registerCommand(String name) {
        try {
            Field commandMapField = BootStrap.PLUGIN.getServer().getClass().getDeclaredField("commandMap");
            commandMapField.setAccessible(true);
            SimpleCommandMap commandMap = (SimpleCommandMap) commandMapField.get(BootStrap.PLUGIN.getServer());

            PluginCommand command = BootStrap.PLUGIN.getCommand(name);
            if (command == null) {
                Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
                constructor.setAccessible(true);
                command = constructor.newInstance(name, BootStrap.PLUGIN);
            }

            command.setExecutor(getCommandExecutor());
            commandMap.register(BootStrap.PLUGIN.getDescription().getName(), command);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
